/******************************************************************************/
/*! 
    Filename
    [Copyright]

    \file
    \namespace    
    \brief      
*/
/******************************************************************************/
/*** Includes *****************************************************************/
#include "NScope.h"
#include "NMemoryManager.h"
#include "NCoreException.h"
/*** Using ********************************************************************/
using namespace Core;
/*** Macros *******************************************************************/
RTTI_DEFINITIONS(NScope)
/*** Constants ****************************************************************/

/*** Declarations *************************************************************/

/*** Variables ****************************************************************/

/*** Implementation ***********************************************************/
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NScope::NScope(void)
	:mParent(NULL),
	 mSize(0)
{
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NScope::NScope(unsigned int initCapacity)
	:mParent(NULL),
	 mSize(0)
{
	ReserveSpace(initCapacity);
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NScope::NScope(const NScope& rhs)
	:mParent(NULL),
	 mSize(0)
{
	*this = rhs;		
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NScope::~NScope(void)
{
	Clear();
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NScope& NScope::operator= (const NScope& rhs)
{
	if(*this != rhs)
	{
		Clear();
		ReserveSpace(rhs.mEntryDictionary.bucket_count());

		NScope & rScope = const_cast<NScope &>(rhs);
		List::iterator _iterator = rScope.mOrderedList.begin();
		for(;_iterator != rhs.mOrderedList.end();_iterator++)
		{
			NDatum& currentDatum = *(*_iterator).second;
			if(currentDatum.GetType() == NDatum::eTYPE_SCOPE)
			{
				for(unsigned int j = 0; j < currentDatum.GetNumValues() ; j++)//deep copy
				{
					if(*currentDatum.Get<NScope*>(j) != rhs)
					{
						NScope* addedScope = MY_NEW(Memory::HID_Engine,"NScope::Assignment::NewScope") NScope();
						*addedScope	= *currentDatum.Get<NScope*>(j);
						Adopt(addedScope, (*_iterator).first, j);
					}
					else
					{
						Adopt(this, "This", j);
					}
				}
			}
			else
			{
				NDatum & addedDatum = Append((*_iterator).first);
				addedDatum = *(*_iterator).second;
			}
		}
	}
	return *this;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
bool NScope::operator == (const NScope& rhs) const
{
	//early checks
	if(mSize == 0 && rhs.mSize == 0) 
	{
		return true;
	}
	if((mSize != rhs.mSize) || (mParent != rhs.mParent))
	{
		return false;
	}
	
	//check order and use datum compare
	NScope & rScope = const_cast<NScope &>(rhs);
	List::iterator search = rScope.mOrderedList.begin();
	for(int i = 0;search != rhs.mOrderedList.end();i++,search++)
	{
		if(mOrderedList[i].first == (*search).first)
		{
			if(mOrderedList[i].second != (*search).second)
			{
				return false;
			}
		}
	}
	return true;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
bool NScope::operator != (const NScope & rhs) const 
{	
	return !operator == (rhs); 
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NDatum& NScope::operator[](std::string name)
{
	return Append(name);
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NDatum& NScope::operator[](unsigned int index)
{
	BoundsCheck(index);
	return *mOrderedList[index].second;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::ReserveSpace(unsigned int size)
{
	mOrderedList.reserve(size);
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::CopyValues(const NScope& aScope, bool overWrite)
{
	//copies content and appends to the end of the this table
	for(unsigned int i = 0; i < aScope.mSize; i++)
	{
		NDatum* found = Find(aScope.mOrderedList[i].first); //search for entry with same name
		CheckVectorSize();

		if(found)//if datum paired with the same name was found
		{
			NDatum& aDatum = *aScope.mOrderedList[i].second;
			//if the have the same type and overwrite if true
			if( found->GetType() == aDatum.GetType() && overWrite)			//overwrite already entry in table
			{
				int indexToOverwrite = Index_Find(aScope.mOrderedList[i].first);					//get index of entry with corresponding name 
				mOrderedList[indexToOverwrite] = aScope.mOrderedList[i];									//over write entry
			}
			else if( found->GetType() != aDatum.GetType() && overWrite)	//if entries have same name but different type
			{
				throw NCoreException("Same name different type: overwrite invalid");
			}
		}
		else //add to end of table 
		{
			std::string key = aScope.mOrderedList[i].first;
			NDatum& datum = *aScope.mOrderedList[i].second;

			Dictionary_Pair dirPair(key , datum);
			mEntryDictionary.insert(dirPair);

			Dictionary::iterator _iterator = mEntryDictionary.find(aScope.mOrderedList[i].first);	//add pair to member hashmap
			List_Pair entry((*_iterator).first,&(*_iterator).second);
			mOrderedList[mSize] = entry;															//add to mOrderedList													
			mSize++;																				//increase table size variable
		}
	}
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NDatum* NScope::Find(std::string name) const
{
	List::const_iterator _iterator = mOrderedList.begin(); 

	for(int i = 0;_iterator != mOrderedList.end(); i++, _iterator++)//walk member vector and search for pair with given name
	{	
		if((*_iterator).first == name)
		{	
			return (*_iterator).second;	//return index of found name
		} 
	}
	return NULL;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
int NScope::Index_Find(std::string name) const
{
	List::const_iterator _iterator = mOrderedList.begin();

	for(int i = 0; _iterator != mOrderedList.end(); i++, _iterator++)//walk member vector and search for pair with given name
	{	
		if((*_iterator).first == name)
		{	
			return i;	//return index of found name
		} 
	}
	return -1;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
bool NScope::Search(std::string name, NDatum** aDatum, NScope** foundScope) const
{
	if(aDatum == 0)		//search self
	{
		NDatum* tempDatum = Find(name);
		if(tempDatum != NULL)
		{
			return true;
		}
	}
	else
	{
		*aDatum = Find(name);
		if(*aDatum != NULL)
		{
			if(foundScope != NULL)*foundScope = const_cast<NScope *>(this);
			return true;
		}
	}
	if(mParent != NULL)//search parent
	{
		if(mParent->Search(name, aDatum , foundScope))
		{
			return true;
		}
	}
	return false;//not found
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NDatum& NScope::Append(std::string datumName)
{
	CapcityCheck();
	CheckVectorSize();

	NDatum* aDatum = NULL;											//create datum to return
	if(Search(datumName,&aDatum))									//find uDataum other wise create one
	{
		return *aDatum;												//if found newDatum now points to stored datum
	}

	NDatum newDatum;													//newDatum point to MY_NEW(5,"") datum: type unknown
	Dictionary_Pair newPair(datumName,newDatum);					//create new name-values pair
	mEntryDictionary.insert(newPair);											//add to mEntryDictionary
	
	Dictionary::iterator _iterator = mEntryDictionary.find(newPair.first);
	if(_iterator != mEntryDictionary.end())
	{
		//NDatum& aDatum = (*_iterator).second;
		List_Pair entry((*_iterator).first,&(*_iterator).second);
		mOrderedList.push_back(entry);									//add to mOrderedList
		mSize++;
		mOrderedList[mSize-1] = entry;									//add to mOrderedList
	}
	else
	{
		throw NCoreException();
	}
	return (*_iterator).second;										//return MY_NEW(5,"") uDataum
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
NScope& NScope::AppendScope(std::string name, unsigned int index)
{
	CapcityCheck();
	CheckVectorSize();

	//search 
	NDatum * found = Find(name);										//search scope for name-value pair with given name
	if(found)														//found == NULL if not found
	{
		if(found->GetType() == NDatum::eTYPE_SCOPE)					//datum is of type table..should be anyway
		{
			BoundsCheck(index);
			return *found->Get<NScope*>(index);						//return MY_NEW(5,"") NScope
		}
		else
		{
			throw std::exception("Name already paired");
		}
	}

	NDatum newDatum(NDatum::eTYPE_SCOPE);			//datum not found so create one
	Dictionary_Pair newEntry(name,newDatum);	//new name-value pair
	mEntryDictionary.insert(newEntry);

	Dictionary::iterator _iterator = mEntryDictionary.find(newEntry.first);	//add to HashMap
	NDatum& datumInDictionary = (*_iterator).second;
	NScope* scopeToAdd  = MY_NEW(Memory::HID_Engine,"NScope::AppendScope::NewScope") NScope();
	scopeToAdd->mParent = this;
	datumInDictionary.Set(scopeToAdd);


	List_Pair listEntry((*_iterator).first,&(*_iterator).second);
	mOrderedList[mSize] = listEntry;									//add to Vector
	mSize++;

	NScope& returnValue = *datumInDictionary.Get<NScope*>(0);
	return returnValue;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::Adopt(NScope* aScope, std::string name, unsigned int index)
{
	CapcityCheck();
	BoundsCheck(index);
	CheckVectorSize();

	if(SearchForParent(*aScope))//check ancestry so that you do not adopt one of your parents
	{	
		throw NCoreException("Cannot allow child to adopt parent");	
	}

	NDatum* added  = Find(name);
	if(added != NULL)
	{		
		throw NCoreException("name already paired");
	}

	if(index == mSize)//add to the end of table
	{	
		NDatum & newestDatum = Append(name);
		newestDatum.Set(aScope);

		if(aScope != this)
		{
			aScope->mParent = this;
		}
	}
	else
	{		
		NDatum newDatum;//new datum of type scope
		newDatum.Set(aScope);
		
		List tempVector;//construct new vector 
		tempVector.reserve(mOrderedList.capacity());
		
		Dictionary_Pair listEntry(name,newDatum);				//new pair

		//add new pair to uData hash map
		mEntryDictionary.insert(listEntry);

		Dictionary::iterator _iterator = mEntryDictionary.find(listEntry.first);

		//rebuild vector with new entry
		for(unsigned int i = 0; i < mSize+1; i++)
		{
			if(i == index)
			{	
				List_Pair listEntry((*_iterator).first,&(*_iterator).second);
				tempVector.push_back(listEntry);
			}
			else
			{	
				tempVector.push_back(mOrderedList[i-1]);
			} 
		}

		//copy temp vector to member vector
		mOrderedList = tempVector;
		mSize  = tempVector.size();
		
		//parent new scope to this scope
		NScope* parrentScope = mOrderedList[index].second->Get<NScope*>()->mParent;
		if(parrentScope != this)
		{
			mOrderedList[index].second->Get<NScope*>()->mParent = this;
		}
	}
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
bool NScope::ReverseFind(NScope* findScope, char** nameFound)
{
	if(mSize == 0)
	{
		return false;
	}

	for(unsigned int i = 0; i < mSize ;i++)													//searches for any children
	{
		NDatum& aDatum = *mOrderedList[i].second;
		if(aDatum.GetType() == NDatum::eTYPE_SCOPE)											//if datum if of type table child found
		{
			for(unsigned int j = 0; j < aDatum.GetNumValues();j++)
			{
				if(aDatum.Get<NScope*>(j) != NULL)
				{
					if(*aDatum.Get<NScope*>(j)  == *findScope)								//if datum points to the same scope continue
					{
						*nameFound = const_cast<char*>(mOrderedList[i].first.c_str());	//set the pointer pointed at by nameFound set it to the name of pair
						return true;
					}
				}
			}
		}
	}
	return false;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::Clear()
{
	Orphan();
	
	if(mSize == 0) return;
	
	unsigned currentSize = mSize;

	for(unsigned int i = 0; i < currentSize;i++)//delete nested scope
	{
		std::string name = mOrderedList[i].first;
		NDatum* aDatum = mOrderedList[i].second;
		if( aDatum->GetType() == NDatum::eTYPE_SCOPE)
		{
			for(unsigned int j = 0; j < aDatum->GetNumValues();j++)
			{
				NScope* aScope = aDatum->Get<NScope*>(j);
				if(aScope != this)
				{
					//MY_DELETE( aScope ); 
				}
			}
		}
	}

	mOrderedList.clear();
	mEntryDictionary.clear();
	mSize = 0;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
std::string NScope::GetMemberName(unsigned int index) const
{
	BoundsCheck(index);
	return mOrderedList[index].first;
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::Orphan()
{
	if(mParent != NULL)
	{
		if(mParent == this)
		{
			throw NCoreException();
		}
		mParent->RemoveScope(*this); //goes to parent and null pointer to self
		mParent = NULL; 
	}
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::CheckVectorSize()
{
	if(mSize == 0)
	{
		ReserveSpace(1);
	}

	if(mOrderedList.capacity() == mSize)
	{	
		mOrderedList.reserve( SIZE_MULTIPLIER * mSize );	
	}
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::RemoveScope(NScope& aScope)
{
	char* foundName = NULL;
	ReverseFind(&aScope,&foundName); //find child

	if(foundName == NULL)
	{
		return;
	}

	NDatum * holder = Find(foundName);
	if(holder != NULL)
	{
		for(unsigned int i = 0; i < holder->GetNumValues();++i)
		{	
			if(*holder->Get<NScope*>(i) == aScope)
			{
				//throw NException("Scope::Remove Scope::Not Implemented");
				mSize--;
			}
		}
	}
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
bool NScope::SearchForParent(NScope& rhs) const
{
	//wants to check if rhs is a parent in any higher up
	if(mParent != NULL && (*mParent == rhs || mParent->SearchForParent(rhs)))	
	{	
		return true;
	}
	return false;
}

/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::BoundsCheck(unsigned int index) const
{
	if(index > mSize)
	{
		throw std::exception("Index Out of Bounds");
	}
}
/******************************************************************************/
/*! [Class] :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void NScope::CapcityCheck()
{
	if(mOrderedList.capacity() == 0)
	{
		ReserveSpace(1);
	}
}